package org.filecollector.filecontroller.basicsfilecontroller;

import lombok.AllArgsConstructor;
import lombok.NonNull;
import org.filecollector.filecontroller.operationresults.OperationResponse;
import org.filecollector.filecontroller.virtualcatalogue.VirtualCatalogue;
import org.filecollector.filecontroller.virtualcatalogue.VirtualCatalogueFactory;
import org.filecollector.filecontroller.virtualcatalogue.groupname.GroupName;

/**
 * Implementation of Default Crud
 * <p>
 *  Every method has`api note with tag and commentary that describes why added this tag
 * </p>
 * <li><u>USER</u> for user calls</li>
 * <li><u>ADMIN</u> for api calls</li>
 */
@AllArgsConstructor
public class BasicsFileControllerClass implements BasicsFileController {

    BasicsFileControllerDao data;
    BasicsFileControllerDependencies dependencies;

    private boolean containsId(Long key) {
        return data.catalogueMap().containsKey(key);
    }

    private VirtualCatalogue getVC(Long key) {
        return data.catalogueMap().get(key);
    }

    private void remove(Long key) {
        data.catalogueMap().remove(key);
    }

    /**
     * Create file and fills it with buffer
     * @param id file id
     * @param buffer buffer that will be written to new file
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccess()} if everything is OK </li>
     *     <li> {@link OperationResponse#constructWrongId()} if incorrect ID </li>
     *     <li> {@link OperationResponse#constructSomethingGoesWrong()} if it needs to call to TroublesHunter</li>
     *     </uL>
     * @apiNote <u>ADMIN</u> - because id for new files is distributing by system and therefore creates by it.
     */
    @Override
    public OperationResponse createBuf(Long id, String buffer) {
        return BasicsFileControllerStaticMethods.createBuf(
                id,
                buffer,
                dependencies.tracer(),
                dependencies.informer(),
                dependencies.warner(),
                this::containsId,
                this::getVC
        );
    }

    /**
     * Create empty id
     * @param id file id
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccess()} if everything is OK </li>
     *     <li> {@link OperationResponse#constructWrongId()} if incorrect ID </li>
     *     <li> {@link OperationResponse#constructSomethingGoesWrong()} if it needs to call to TroublesHunter</li>
     *     </uL>
     * @apiNote <u>ADMIN</u> - because id for new files is distributing by system and therefore creates by it.
     */
    @Override
    public OperationResponse createEmpty(Long id) {
        return BasicsFileControllerStaticMethods.createEmpty(
                id,
                dependencies.tracer(),
                dependencies.informer(),
                dependencies.warner(),
                this::containsId,
                this::getVC
        );
    }

    /**
     * Create new catalogue with {@code namePath}
     * @param namePath path name of new file
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccess()} if everything is OK </li>
     *     <li> {@link OperationResponse#constructWrongId()} if incorrect ID </li>
     *     <li> {@link OperationResponse#constructSomethingGoesWrong()} if it needs to call to TroublesHunter</li>
     *     <li> {@link OperationResponse#constructCatalogueAlreadyTracking()} if catalogue already tracking</li>
     *     <li> {@link OperationResponse#constructWrongCatalogueName()} if catalogue intersects to one of tracking</li>
     *     </uL>
     * @apiNote <u>ADMIN</u> - catalogues controls only by system
     */
    @Override
    public OperationResponse createCatalogue(@NonNull String namePath) {
        return BasicsFileControllerStaticMethods.createCatalogue(
                namePath,
                dependencies.tracer(),
                dependencies.informer(),
                () -> VirtualCatalogueFactory.constructFromString(
                        namePath,
                        dependencies.virtualCatalogueDependenciesProducer().apply(namePath)
                ),
                data.groupName()::includes,
                (GroupName name) -> data.catalogueMap().keySet().stream().anyMatch(name::isIntersected),
                (VirtualCatalogue vc) -> data.catalogueMap().put(vc.getName(), vc)
        );
    }

    /**
     * Read file with id of size
     * @param id id of ile
     * @param size size of returning buffer
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccessBuffered(char[])} if everything is OK </li>
     *     <li> {@link OperationResponse#constructWrongId()} if incorrect ID </li>
     *     <li> {@link OperationResponse#constructSomethingGoesWrong()} if it needs to call to TroublesHunter</li>
     *     <li> {@link OperationResponse#constructExceptionThrew()}  if exception thrown and unhatched</li>
     *     </uL>
     * @apiNote <u>USER</u>
     */
    @Override
    public OperationResponse readBuf(Long id, int size) {
        return BasicsFileControllerStaticMethods.readBuf(
                id,
                size,
                dependencies.tracer(),
                dependencies.informer(),
                this::containsId,
                this::getVC
        );
    }

    /**
     * Return all ids of catalogue with id
     * @param id catalogue id
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccessBuffered(char[])} if everything is OK </li>
     *     <li> {@link OperationResponse#constructWrongId()} if incorrect ID </li>
     *     <li> {@link OperationResponse#constructNotSuchId()} if this ID doesn't exist</li>
     *     </uL>
     * @apiNote <u>ADMIN</u> - only system has access to catalogues
     */
    @Override
    public OperationResponse readIdGroup(@NonNull Long id) {
        return BasicsFileControllerStaticMethods.readIdGroup(
                id,
                dependencies.tracer(),
                dependencies.informer(),
                this::containsId,
                this::getVC
        );
    }

    /**
     * Get all tracking catalogues
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccessBuffered(char[])} if everything is OK </li>
     *     </uL>
     * @apiNote <u>ADMIN</u> - only system has access to catalogues
     */
    @Override
    public OperationResponse readGroups() {
        return BasicsFileControllerStaticMethods.readGroups(
                dependencies.tracer(),
                data.catalogueMap().keySet()::stream
        );
    }

    /**
     * list all tracking ids
     * @return <uL>
     *        <li> {@link OperationResponse#constructSuccessBuffered(char[])} if everything is OK </li>
     *        </uL>
     * @apiNote <u>ADMIN</u> - only system can get access to list of all id
     */
    @Override
    public OperationResponse readAllIds() {
        return BasicsFileControllerStaticMethods.readAllIds(
                dependencies.tracer(),
                data.catalogueMap().values()::stream
        );
    }

    /**
     * List all free ids by group Id
     * @param groupId id of catalogue
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccessBuffered(char[])} if everything is OK </li>
     *     <li> {@link OperationResponse#constructWrongId()} if incorrect ID </li>
     *     </uL>
     * @apiNote <u>ADMIN</u> - only system can get access to list of all id
     */
    @Override
    public OperationResponse readFreeIds(@NonNull Long groupId) {
        return BasicsFileControllerStaticMethods.readFreeIds(
                groupId,
                dependencies.tracer(),
                dependencies.informer(),
                this::containsId,
                (Long a) -> getVC(a).listFreeDiapasons()
        );
    }

    /**
     * Update file with id and renew it with buffer
     * @param id file
     * @param buffer new content of file
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccess()} if everything is OK </li>
     *     <li> {@link OperationResponse#constructWrongId()} if incorrect ID </li>
     *     <li> {@link OperationResponse#constructNotSuchId()} if ID doesn't exist</li>
     *     <li> {@link OperationResponse#constructExceptionThrew()} exception threw and uncouth</li>
     *     <li> {@link OperationResponse#constructSomethingGoesWrong()} if something went wrong</li>
     *     </uL>
     * @apiNote <u>USER</u>
     */
    @Override
    public OperationResponse updateBuf(Long id, char[] buffer) {
        return BasicsFileControllerStaticMethods.updateBuf(
                id,
                buffer,
                dependencies.tracer(),
                dependencies.informer(),
                this::containsId,
                this::getVC
        );
    }

    /**
     * Update file with id and append it with buffer
     * @param id file
     * @param buffer new content of file
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccess()} if everything is OK </li>
     *     <li> {@link OperationResponse#constructWrongId()} if incorrect ID </li>
     *     <li> {@link OperationResponse#constructNotSuchId()} if ID doesn't exist</li>
     *     <li> {@link OperationResponse#constructExceptionThrew()} exception threw and uncouth</li>
     *     <li> {@link OperationResponse#constructSomethingGoesWrong()} if something went wrong</li>
     *     </uL>
     * @apiNote <u>USER</u>
     */
    @Override
    public OperationResponse updateAppendBuf(@NonNull Long id, @NonNull char[] buffer) {
        return BasicsFileControllerStaticMethods.updateAppendBuf(
                id,
                buffer,
                dependencies.tracer(),
                dependencies.informer(),
                this::containsId,
                this::getVC
        );
    }

    /**
     * Delete file by id
     * @param id id of deleting file
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccess()} if everything is OK </li>
     *     <li> {@link OperationResponse#constructWrongId()} if incorrect ID </li>
     *     <li> {@link OperationResponse#constructNotSuchId()} if ID doesn't exist</li>
     *     <li> {@link OperationResponse#constructFileAlreadyRemoved()} if file removed</li>
     *     </uL>
     * @apiNote <u>USER</u>
     */
    @Override
    public OperationResponse delete(Long id) {
        return BasicsFileControllerStaticMethods.delete(
                id,
                dependencies.tracer(),
                dependencies.informer(),
                this::containsId,
                this::getVC
        );
    }

    /**
     * Delete catalogue by id due to removing catalogue by id from CatalogueMap
     * @param anyId id of deleting catalogue
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccess()} if everything is OK </li>
     *     </uL>
     * @apiNote <u>ADMIN</u> - only system has access to catalogues
     */
    @Override
    public OperationResponse deleteCatalogueSoft(Long anyId) {
        return BasicsFileControllerStaticMethods.deleteCatalogueSoft(
                anyId,
                dependencies.tracer(),
                this::remove
        );
    }

    /**
     * Deeply removing of tracking catalogue by id
     * @param anyId id of deleting catalogue
     * @return <uL>
     *     <li> {@link OperationResponse#constructSuccess()} if everything is OK </li>
     *     </uL>
     * @apiNote <u>ADMIN</u> - only system has access to catalogues
     */
    @Override
    public OperationResponse deleteCatalogueHard(@NonNull Long anyId) {
        return BasicsFileControllerStaticMethods.deleteCatalogueHard(
                anyId,
                dependencies.tracer(),
                dependencies.informer(),
                this::containsId,
                this::getVC,
                this::remove
        );
    }
}
